﻿// ============================================================================
// FileName: SIPRegistrarConsole.cs
//
// Description:
// Main console display for the demonstration SIP Registrar.
//
// Author(s):
// Aaron Clauson
//
// History:
// 21 Aug 2008	Aaron Clauson	Created.
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2008 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
// ============================================================================

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Xml;
using BlueFace.Sys.Net;
using BlueFace.VoIP.Net.SIP;
using BlueFace.VoIP.SIPServer;
using log4net;

namespace SIPSwitch.SIPRegistrar
{
    class SIPRegistrarConsole
    {
        private const string REGISTRAR_SERVER_HEADER = "mysipswitch.com demo registrar";
        private const int NATKEEPALIVE_INTERVAL_SECONDS = 15;
        private const int MAXIMUM_PERUSER_BINDINGS = 5;         // The maximum number of contacts per SIP account that will be recorded.

        private static ILog logger = SIPRegistrarState.logger;

        private static XmlNode SIPSocketsNode = SIPRegistrarState.SIPSocketsNode;
        
        private static string m_natKeepAliveProxySocket = SIPRegistrarState.NATKeepAliveProxySocket;    // An optional setting that specifies a socket on a SIP Proxy process that can be used to forward NAT Keep Alive requests.
        private static int m_monitorEventPort = SIPRegistrarState.MonitorEventPort;                     // An optional setting that if specified holds a loopback port number to send events to for a separate monitoring process.
        
        private static ProxyMonitorChannel m_monitorChannel;    // If the monitor event port is specified this channel will be created to send the events.                     
        private static IPEndPoint m_natKeepAliveEndPoint;
        private static UdpClient m_natKeepAliveSender;          // if the NAT Keep Alive socket is specified this UDP socket will be sued to forward the requests to the proxy.

        // The program will prompt the user when a new REGISTER request is received. If it is then it will be stored in this list and accepted for the duration.
        private static Dictionary<string, SIPRegistrarRecord> m_acceptedAORs = new Dictionary<string, SIPRegistrarRecord>();

        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Demonstration SIP Registrar for MySIPSwitch SIP Stack (www.mysipswitch.com).");

                // Pre-flight checks.
                if (SIPSocketsNode == null || SIPSocketsNode.ChildNodes.Count == 0)
                {
                    throw new ApplicationException("The registrar cannot start without at least one socket specified to listen on, please check config file.");
                }

                // Want SIP transaction state on SIP Registrar so duplicate registration requests can be ignored.
                SIPTransactionEngine transactionEngine = new SIPTransactionEngine();   
                
                // Initialise the SIP Transport layer and set up all the socket listeners.
                SIPTransport sipTransport = new SIPTransport(transactionEngine, false, false);
                foreach (XmlNode sipSocketNode in SIPSocketsNode.ChildNodes)
                {
                    SIPUDPChannel udpChannel = new SIPUDPChannel(IPSocket.ParseSocketString(sipSocketNode.InnerText));
                    sipTransport.AddSIPChannel(udpChannel);
                }

                #region Optionally enable monitoring and event logging.

                // The monitor channel is completely optional and whether it is specified or not will not change the registrar's behaviour.
                // Send events from this process to the monitoring socket.
                if (m_monitorEventPort != 0)
                {
                    // Events will be sent by the monitor channel to the loopback interface and this port.
                    m_monitorChannel = new ProxyMonitorChannel(m_monitorEventPort);
                    logger.Debug("Monitor channel initialised for 127.0.0.1:" + m_monitorEventPort + ".");
                }

                sipTransport.SIPRequestInTraceEvent += (protocol, localEndPoint, endPoint, sipRequest) => RegistrarLog(new ProxyMonitorEvent(ProxyServerTypesEnum.Registrar, ProxyEventTypesEnum.FullSIPTrace, null, sipRequest, null, localEndPoint, endPoint, SIPCallDirection.In));
                sipTransport.SIPRequestOutTraceEvent += (protocol, localEndPoint, endPoint, sipRequest) => RegistrarLog(new ProxyMonitorEvent(ProxyServerTypesEnum.Registrar, ProxyEventTypesEnum.FullSIPTrace, null, sipRequest, null, localEndPoint, endPoint, SIPCallDirection.Out));
                sipTransport.SIPResponseInTraceEvent += (protocol, localEndPoint, endPoint, sipResponse) => RegistrarLog(new ProxyMonitorEvent(ProxyServerTypesEnum.Registrar, ProxyEventTypesEnum.FullSIPTrace, null, null, sipResponse, localEndPoint, endPoint, SIPCallDirection.In));
                sipTransport.SIPResponseOutTraceEvent += (protocol, localEndPoint, endPoint, sipResponse) => RegistrarLog(new ProxyMonitorEvent(ProxyServerTypesEnum.Registrar, ProxyEventTypesEnum.FullSIPTrace, null, null, sipResponse, localEndPoint, endPoint, SIPCallDirection.Out));

                // The Registrar should never receive a SIP response. It delas with REGISTER requests only.
                sipTransport.SIPTransportResponseReceived += (protocol, localEndPoint, endPoint, sipResponse) => RegistrarLog(new ProxyMonitorEvent(ProxyServerTypesEnum.Registrar, ProxyEventTypesEnum.Warn, "Unexpected Response on Registrar (" + sipResponse.Status + " " + sipResponse.Header.CSeqMethod + "): " + localEndPoint + "->" + endPoint, null)); 
                
                #endregion

                #region Optionally configure NAT keep-alive sending.

                if (m_natKeepAliveProxySocket != null)
                {
                    try
                    {
                        m_natKeepAliveEndPoint = IPSocket.ParseSocketString(m_natKeepAliveProxySocket);

                        if (m_natKeepAliveEndPoint != null)
                        {
                            m_natKeepAliveSender = new UdpClient();
                            logger.Debug("NATKeepAlives for SIP Registrar enabled, endpoint is " + m_natKeepAliveEndPoint + ".");
                        }
                        else
                        {
                            logger.Warn("NAT Keep Alive socket could not be parsed from " + m_natKeepAliveProxySocket + ".");
                        }
                    }
                    catch (Exception excp)
                    {
                        logger.Error("Exception creating NATKeepAlive sender. " + excp.Message);
                    }
                }

                #endregion

                // In a real Registrar the SIP accounts would be looked up in a database or file but for this demo we'll let the user decide on the console.
                SIPRegistrations registrationsStore = new SIPRegistrations(null, RegistrarLog, LoadRegistrarRecord, null, SendNATKeepAlive);
                registrationsStore.StartCheckContacts();

                // We'll accept any expiry the user agent requests up to a maximum of 3600.
                Dictionary<string, SIPUserAgentConfiguration> userAgentExpiries = new Dictionary<string, SIPUserAgentConfiguration>();
                userAgentExpiries.Add(".*", new SIPUserAgentConfiguration(3600, false, ".*"));

                RegistrarCore registrarCore = new RegistrarCore(
                    sipTransport,
                    registrationsStore,
                    true,               // Mangle the Contact header in the REGISTER requests to reflect the IP it was received on. Used to take account of NAT'ted private IP addresses.
                    false,              // For this demo we'll accept registrations for any domains/realms. If the acceptances needed to be restricted then this would be set to true and a list of allowed domains configured.
                    REGISTRAR_SERVER_HEADER,
                    MAXIMUM_PERUSER_BINDINGS,
                    RegistrarLog,
                    userAgentExpiries);

                // Events that pass the SIP requests and responses onto the Registrar Core.
                sipTransport.SIPTransportRequestReceived += new SIPTransportRequestReceivedDelegate(registrarCore.AddRegisterRequest);

                Console.WriteLine("SIP Registrar running, ctrl-c to exit...");

                ManualResetEvent stayAliveEvent = new ManualResetEvent(false);
                stayAliveEvent.WaitOne();

                // Normally the sockets would be gracefully shutdown but with a ctrl-c exit the OS will reclaim everything from the killed process.
                //sipTransport.Shutdown();
            }
            catch (Exception excp)
            {
                Console.WriteLine("Exception Main. " + excp.Message);
                Console.WriteLine("press any key to exit...");
                Console.ReadLine();
            }
        }

        private static SIPRegistrarRecord LoadRegistrarRecord(SIPParameterlessURI addressOfRecord)
        {
            if (m_acceptedAORs.ContainsKey(addressOfRecord.ToString()))
            {
                return m_acceptedAORs[addressOfRecord.ToString()];
            }
            else
            {
                Console.Write("Would you like to accept a registration for " + addressOfRecord.ToString() + " ? ");
                string accept = Console.ReadLine();

                if (accept == "y" || accept == "Y")
                {
                    SIPRegistrarRecord registrarRecord = new SIPRegistrarRecord(RegistrarLog, Guid.NewGuid(), addressOfRecord, addressOfRecord.User, null, true);
                    m_acceptedAORs.Add(addressOfRecord.ToString(), registrarRecord);
                    return registrarRecord;
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// This event handler is fired when the SIP Registrar wants to send a NATKeepAlive message to a user contact to make an attempt to keep the connection
        /// open through the user's NAT server. In this case the NATKeepAlive messages are forwarded onto the NATKeepAlive relay which requests the SIPTransport
        /// layer to multiplex a 4 byte null payload onto one of its sockets and send to the specified agent. A typical scenario would have the SIP Registrar
        /// firing this event every 15s to send the null payloads to each of its registered contacts.
        /// </summary>
        private static void SendNATKeepAlive(NATKeepAliveMessage keepAliveMessage)
        {
            try
            {
                if (m_natKeepAliveSender != null && m_natKeepAliveEndPoint != null)
                {
                    byte[] buffer = keepAliveMessage.ToBuffer();
                    m_natKeepAliveSender.Send(buffer, buffer.Length, m_natKeepAliveEndPoint);
                }
            }
            catch (Exception natSendExcp)
            {
                logger.Error("Exception SendNATKeepAlive " + keepAliveMessage.SendToSocket + ". " + natSendExcp.Message);
            }
        }

        private static void RegistrarLog(ProxyMonitorEvent monitorEvent)
        {
            try
            {
                if (monitorEvent.EventType != ProxyEventTypesEnum.FullSIPTrace)
                {
                    logger.Debug(monitorEvent.Message);
                }

                // If the monitor channel has been configured send the event.
                if (m_monitorChannel != null)
                {
                    m_monitorChannel.Send(monitorEvent);
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception RegistrarLog. " + excp.Message);
            }
        }
    }
}
